﻿// ------------------------------------------------------------------------
// 项目名称：Canroc.Net 
// 版权归属：Canroc（https://gitee.com/canroc）
//
// 许可证信息
// Canroc.Net项目主要遵循 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-APACHE 文件。
//
// 使用条款：
// 1.使用本项目应遵守相关法律法规和许可证的要求。
// 2.不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动。
// 3.任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
//
// 其他重要信息
// Canroc.Net 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// ------------------------------------------------------------------------

using Canroc.Net.Application.SysSequenceService.Dto;

// ReSharper disable once CheckNamespace
namespace Canroc.Net.Application.SysSequenceService;

/// <summary>
///     系统序列应用服务
/// </summary>
[ApiDescriptionSettings("System")]
public partial class SysSequenceAppService(
    SqlSugarRepository<SysSequence> sysSequenceRep,
    SqlSugarRepository<SysSequenceRule> sysSequenceRuleRep) : IDynamicApiController, ITransient
{
    private readonly SemaphoreSlim _semaphore = new(1);

    /// <summary>
    ///     添加系统序列
    /// </summary>
    /// <param name="data">表单数据</param>
    /// <returns></returns>
    public async Task<bool> AdditionAsync(SequenceInput data)
    {
        var entity = data.Adapt<SysSequence>();
        return await sysSequenceRep.InsertAsync(entity);
    }

    /// <summary>
    ///     编辑系统序列
    /// </summary>
    /// <param name="data">表单数据</param>
    /// <returns></returns>
    public async Task<bool> EditAsync(SequenceInput data)
    {
        var entity = await sysSequenceRep.GetByIdAsync(data.Id);
        data.Adapt(entity);
        return await sysSequenceRep.UpdateAsync(entity);
    }

    /// <summary>
    ///     启用/禁用系统序列
    /// </summary>
    /// <param name="data">请求参数</param>
    /// <returns></returns>
    public async Task<bool> EnableAsync(InputModel data)
    {
        return await sysSequenceRep.UpdateAsync(
            s => new SysSequence { IsEnable = SqlFunc.IIF(s.IsEnable, false, true) }, s => s.Id == data.Id);
    }

    /// <summary>
    ///     删除系统序列
    /// </summary>
    /// <param name="data">请求参数</param>
    /// <returns></returns>
    public async Task<bool> DelAsync(DeleteInputModel data)
    {
        return await sysSequenceRep.UpdateAsync(s => new SysSequence { IsDelete = true },
            s => data.Ids.Contains(s.Id));
    }

    /// <summary>
    ///     获取系统序列
    /// </summary>
    /// <param name="id">主键</param>
    /// <returns></returns>
    public async Task<SequenceOutput?> GetDataAsync(Guid id)
    {
        return await sysSequenceRep.AsQueryable().Where(s => s.Id == id).Select<SequenceOutput>().FirstAsync();
    }

    /// <summary>
    ///     分页: 系统序列集合
    /// </summary>
    /// <param name="query">请求参数</param>
    /// <returns></returns>
    public async Task<PageModel<SequenceOutput>> GetPageAsync([FromQuery] SequencePageQuery query)
    {
        return await sysSequenceRep
            .AsQueryable()
            .WhereIF(!query.Name.IsNullOrEmpty(), r => r.Name.Contains(query.Name))
            .WhereIF(query.IsEnable.HasValue, r => r.IsEnable == query.IsEnable)
            .OrderByPropertyName(query.OrderField, query.IsAscending ? OrderByType.Asc : OrderByType.Desc)
            .Select<SequenceOutput>()
            .ToPageAsync(query.PageIndex, query.PageSize);
    }

    /// <summary>
    ///     获取最新业务单据编码
    /// </summary>
    /// <param name="sequenceName">业务单据编码名称</param>
    /// <returns></returns>
    [NonAction]
    public async Task<string> GetSequenceNextAsync(string sequenceName)
    {
        // 限制并发请求
        await _semaphore.WaitAsync();
        try
        {
            // 生成编号
            var sequenceNewNo = "";

            #region 获取序号生成器属性

            if (string.IsNullOrWhiteSpace(sequenceName))
                throw Oops.Oh("请传入业务编码名称");

            // 获取序号生成器属性
            var sequence =
                await sysSequenceRep.AsQueryable().Where(s => s.Name == sequenceName).FirstAsync() ??
                throw Oops.Oh("请定义" + sequenceName + "的单据编码！");
            var ruleList = await sysSequenceRuleRep.AsQueryable()
                .Where(r => r.SequenceId == sequence.Id).OrderBy(r => r.RuleOrder).ToListAsync();
            if (ruleList is null || ruleList.Count <= 0)
                throw Oops.Oh("未查询到业务编码对应的编码规则配置, 请检查编码规则配置");

            var delimiterNum = 0;
            ruleList.ForEach(item =>
            {
                delimiterNum++;

                switch (item.RuleType)
                {
                    // 常量方式
                    case "const":
                        sequenceNewNo += item.RuleValue;
                        break;
                    // 计数，流水号
                    case "number":
                        var num = CurrentReset(sequence, item);
                        // 计数拼接
                        sequenceNewNo += NumberingSeqRule(item, num).ToString();
                        // 更新当前序号
                        sequence.CurrentNo = num;
                        break;
                    // 日期，年4位
                    case "date":
                        sequenceNewNo += DateTime.Now.ToString("yyyyMMdd");
                        break;
                    // 短日期 年2位月2位日期2位
                    case "shortdate":
                        sequenceNewNo += DateTime.Now.ToString("yyyyMMdd")[2..];
                        break;
                    // 年月
                    case "ydate":
                        sequenceNewNo += DateTime.Now.ToString("yyyyMMdd")[..6];
                        break;
                    // 年月，年2位月2位
                    case "sydate":
                        sequenceNewNo += DateTime.Now.ToString("yyyyMMdd")[2..6];
                        break;
                    // 日期时间精确到毫秒
                    case "timestamp":
                        sequenceNewNo += DateTimeOffset.Now.ToUnixTimeSeconds().ToString();
                        break;
                    // Guid
                    case "guid":
                        sequenceNewNo += Guid.NewGuid().ToString("N");
                        break;
                    // 随机数
                    case "random":
                        Random random = new();
                        var strMax = "9".PadLeft(item.RuleValue.Length, '9');
                        var strRandom = random.Next(item.RuleValue.ParseToInt(), strMax.ParseToInt())
                            .ToString(); //生成随机编号 
                        sequenceNewNo += strRandom;
                        break;
                }

                // 是否拼接分隔符，最后一个不拼接
                if (!sequence.Delimiter.IsNullOrEmpty() && delimiterNum != ruleList.Count)
                    sequenceNewNo += sequence.Delimiter;
            });

            // 更新系统序列当前编号与生成时间
            sequence.CurrentCode = sequenceNewNo;
            sequence.CurrentReset = DateTime.Now.ToString("yyyyMMdd");
            _ = await sysSequenceRep.UpdateAsync(sequence);

            #endregion

            return sequenceNewNo;
        }
        finally
        {
            _semaphore.Release();
        }
    }

    /// <summary>
    ///     计数方式 重置规则
    /// </summary>
    /// <param name="seq">系统序列</param>
    /// <param name="seqRule">序列规则</param>
    /// <returns></returns>
    [NonAction]
    private static int CurrentReset(SysSequence seq, SysSequenceRule seqRule)
    {
        int newNo = 0, ruleNo = seqRule.RuleValue.ParseToInt();
        if (ruleNo == 0)
            newNo = 1;

        switch (seq.SequenceReset)
        {
            case "D": //每天重置
                if (!seq.CurrentReset.IsNullOrEmpty() && seq.CurrentReset != DateTime.Now.ToString("yyyyMMdd"))
                    newNo = 1;

                break;
            case "M": //每月重置
                if (!seq.CurrentReset.IsNullOrEmpty())
                {
                    if (!seq.CurrentReset.Contains(DateTime.Now.ToString("yyyyMM")))
                        newNo = ruleNo;
                }
                else
                {
                    newNo = 1;
                }

                break;
            case "Y": //每年重置
                if (!seq.CurrentReset.IsNullOrEmpty())
                {
                    if (!seq.CurrentReset.Contains(DateTime.Now.ToString("yyyy")))
                        newNo = ruleNo;
                }
                else
                {
                    newNo = 1;
                }

                break;
        }

        if (newNo != 0) return newNo;

        if (seq.CurrentNo == 0)
            newNo = ruleNo;
        // 当前序号+步长 
        else
        {
            newNo = seq.CurrentNo + seq.Step;
        }

        return newNo;
    }

    /// <summary>
    ///     计数规则
    /// </summary>
    /// <param name="seqRule">序列规则</param>
    /// <param name="code">当前序号</param>
    /// <returns></returns>
    [NonAction]
    private static string NumberingSeqRule(SysSequenceRule seqRule, int code)
    {
        var str = code.ToString();
        str = seqRule switch
        {
            { PaddingSide: "left", PaddingWidth: not null } => str.PadLeft(seqRule.PaddingWidth.Value,
                seqRule.PaddingChar.ParseToToChar()),
            { PaddingSide: "right", PaddingWidth: not null } => str.PadRight(seqRule.PaddingWidth.Value,
                seqRule.PaddingChar.ParseToToChar()),
            _ => str
        };

        return str;
    }
}